Step By Step Guide
Step-by-step Guide
Step 1. Create React App
# NPMnpx create-react-app your-app-name# Yarnyarn create react-app your-app-name
# NPMcd your-app-namenpm start# Yarncd your-app-nameyarn start
Step 2. Fetch GraphQL (Relay 없이 fetch() 사용)
2.1. GitHub GraphQL Authentication
# your-app-name/.env.localREACT_APP_GITHUB_AUTH_TOKEN=<TOKEN>
2.2. A fetchGraphQL Helper
// your-app-name/src/fetchGraphQL.jsasync function fetchGraphQL(text, variables) {...// Fetch data from GitHub's GraphQL API:const response = await fetch('https://api.github.com/graphql', {method: 'POST',...,body: JSON.stringify({query: text,variables,}),});// Get the response as JSONreturn await response.json();}export default fetchGraphQL;
2.3. Fetching GraphQL From React
// your-app-name/src/App.js...function App() {// We'll load the name of a repository, initially setting it to nullconst [name, setName] = useState(null);// When the component mounts we'll fetch a repository nameuseEffect(() => {let isMounted = true;fetchGraphQL(`query RepositoryNameQuery {# feel free to change owner/name hererepository(owner: "facebook" name: "relay") {name}}`).then(response => {// Avoid updating state if the component unmounted before the fetch completes// 데이터 패칭이 완료되기 전 unMount 되는 경우 처리if (!isMounted) {return;}const data = response.data;setName(data.repository.name);}).catch(error => {console.error(error);});return () => {isMounted = false;};}, [fetchGraphQL]);// Render "Loading" until the query completesreturn (<div className="App"><header className="App-header"><p>{name != null ? `Repository: ${name}` : "Loading"}</p></header></div>);}export default App;
Step 3. When To Use Relay (Relay 사용해 데이터 가져오기)
step2를 사용하면, 어플리케이션의 규모가 커지는 속도와 크기에 대응하기 어려워진다.
이 때 Relay를 사용하면 빠르고 믿을 수 있는 방식으로 이에 대응할 수 있는데, 이유는 다음과 같다.
- GraphQL 프래그먼트, 데이터 일관성, mutations을 컴포넌트에 위치시켜 데이터 의존성을 모아놓기 (colocating)
Step 4. Adding Relay To Our Project
Relay는 3가지 핵심으로 이루어져있다.
relay-compiler
: 컴파일러 (빌드 타임에 사용됨)relay-runtime
: 코어 런타임 (React 친화적)react-relay
: React integration layer
# NPM Usersnpm install --save relay-runtime react-relaynpm install --save-dev relay-compiler babel-plugin-relay# Yarn Usersyarn add relay-runtime react-relayyarn add --dev relay-compiler babel-plugin-relay
4.1. Configure Relay Compiler
Relay example app의 .graphql
스키마 카피를 다운로드 받기 위해
cd your-app-namecurl https://raw.githubusercontent.com/relayjs/relay-examples/main/issue-tracker/schema/schema.graphql > schema.graphql
package.json 설정은 다음과 같다.
// your-app-name/package.json{..."scripts": {..."start": "yarn run relay && react-scripts start","build": "yarn run relay && react-scripts build","relay": "yarn run relay-compiler $@"...},"relay": {"src": "./src/","schema": "./schema.graphql"}...}
cd your-app-nameyarn start
여기서, GraphQL을 사용하게 되면 Relay는 이를 감지하여 해당 프로젝트에서 작성한 쿼리를 나타내는 코드를 your-app-name/src/__generated__/
에 생성한다.
4.2. Configure Relay Runtime
컴파일러 설정이 완료되었으니 런타임을 세팅할 수 있는데, 이는 Relay에게 우리의 GraphQL 서버와 어떻게 연결할 것인지에 대해 알려주는 것과 같다.
위의 코드를 동일하게 사용하되, 추가적으로 Relay Environment 를 정의한다. 이는 서버 (Relay Network) 에 저장된 캐시를 어떻게 활용할 것인지에 대해 캡슐화하는 것을 의미한다.
// your-app-name/src/RelayEnvironment.jsimport {Environment, Network, RecordSource, Store} from 'relay-runtime';import fetchGraphQL from './fetchGraphQL';// Relay passes a "params" object with the query name and text. So we define a helper function// to call our fetchGraphQL utility with params.text.async function fetchRelay(params, variables) {console.log(`fetching query ${params.name} with ${JSON.stringify(variables)}`);return fetchGraphQL(params.text, variables);}// Export a singleton instance of Relay Environment configured with our network function:export default new Environment({network: Network.create(fetchRelay),store: new Store(new RecordSource()),});
Step 5. Fetching a Query With Relay
...import fetchGraphQL from './fetchGraphQL';import graphql from 'babel-plugin-relay/macro';import {RelayEnvironmentProvider,loadQuery,usePreloadedQuery,} from 'react-relay/hooks';import RelayEnvironment from './RelayEnvironment';const { Suspense } = React;// Define a queryconst RepositoryNameQuery = graphql`query AppRepositoryNameQuery {repository(owner: "facebook", name: "relay") {name}}`;// 앱이 시작하자마자 쿼리를 즉시 로드한다.// 실제 앱에서는 라우팅 configuration에 이를 설정하고, 새로운 route로 이동 시 데이터를 프리-로드한다.const preloadedQuery = loadQuery(RelayEnvironment, RepositoryNameQuery, {/* query variables */});// 프리로드된 쿼리를 읽는 이너 컴포넌트는 `usePreloadedQuery`를 사용한다.// - 쿼리가 실행 완료되면, 쿼리의 결과를 리턴한다.// - 쿼리가 아직 펜딩 중이면, Suspend 한다. 이는 부모 컴포넌트 중 가장 가까이 위치한 fallback에 근거한다.// - 쿼리가 (데이터 패칭을) 실패하면, 실패 에러를 띄운다.function App(props) {const data = usePreloadedQuery(RepositoryNameQuery, props.preloadedQuery);return (<div className="App"><header className="App-header"><p>{data.repository.name}</p></header></div>);}// 위 App 컴포넌트는 어떻게 Relay Environment에 접근할지에 대한 정보가 있어야 하고,// Suspend 경우를 위한 fallback을 설정해야 한다.function AppRoot(props) {return (<RelayEnvironmentProvider environment={RelayEnvironment}><Suspense fallback={'Loading...'}><App preloadedQuery={preloadedQuery} /></Suspense></RelayEnvironmentProvider>);}export default AppRoot;
- RepositoryNameQuery
- 쿼리를 정의한다.
- preloadQuery
- 사전에 정의한 RelayEnvironment와 RepositoryNameQuery, 쿼리 변수(args)를 전달한다.
- AppRoot
<RelayEnvironmentProvider>
은 현재 Relay Environment 인스턴스와의 소통 방식을 child 컴포넌트에 전달한다.<Suspense>
는 child가 suspend할 경우의 fallback을 지정한다.